﻿using System.Collections.Concurrent;
using System.Diagnostics;
using Volo.Abp.DependencyInjection;

namespace FlyingEye.DeviceMonitor
{
    /// <summary>
    /// 任务状态管理器
    /// </summary>
    public class ObjectStatusManager : ITransientDependency
    {
        public ObjectStatusManager()
        {
            _token = Guid.Empty;
        }

        public int TaskStatusCount => _status.Count;

        /// <summary>
        /// 需要一个静态表存储数据库任务的状态
        /// </summary>
        private readonly ConcurrentDictionary<Guid, ObjectStatus> _status = new();
        private int _activeCount = 0;
        private Guid _token;

        /// <summary>
        /// 重置任务状态令牌
        /// </summary>
        public void UpdateObjectStatusToken()
        {
            _activeCount = 0;
            _token = Guid.NewGuid();
        }

        /// <summary>
        /// 清理垃圾任务状态
        /// </summary>
        public void ClearTrashObjectStatus()
        {
            // 如果未检索的任务超过 200 个，那么清除垃圾任务
            if (TaskStatusCount - _activeCount >= 200)
            {
                _status.RemoveAll(item => item.Value.Token != _token);
            }
        }

        /// <summary>
        /// 判断任务是否可以执行
        /// </summary>
        /// <param name="id">任务 id</param>
        /// <param name="interval">任务周期</param>
        public bool CanAccessObject(Guid id, int interval)
        {
            // 原子操作获取当前令牌副本（避免执行过程中令牌被重置）
            var currentToken = _token;

            var status = _status.GetOrAdd(id, _ => new ObjectStatus
            {
                NextTime = DateTimeOffset.UtcNow,
                Token = currentToken
            });

            // 原子更新计数器（替代非原子自增）
            Interlocked.Increment(ref _activeCount);

            // 使用内存屏障保证读取最新状态
            Interlocked.MemoryBarrier();
            status.Token = currentToken;

            var now = DateTimeOffset.UtcNow;
            if (now >= status.NextTime)
            {
                // 时间更新需要原子操作
                var newNextTime = now.AddMilliseconds(interval);
                status.NextTime = newNextTime;
                return true;
            }
            return false;
        }
    }
}
